package net.sourceforge.gomoku

import reflect.BeanProperty
import collection.mutable
import java.util.HashSet
import mutable.{ArrayBuffer, ListBuffer}
import xml.XML
import java.io.{StringReader, BufferedReader, File}

/**
 * Created by IntelliJ IDEA.
 * User: denny
 * Date: Aug 19, 2010
 * Time: 1:48:18 PM
 * To change this template use File | Settings | File Templates.
 */

object DefensivePatterns {
  def main(args:Array[String]){
  }
}

class DefensivePatterns(@BeanProperty val piece: Byte) extends Player{

  var knownPatterns:PatternSet = PatternSet.loadPatternSet
  printf("Known patterns: %d  file=%s %n",knownPatterns.size, PatternSet.patternsFile)

  var losingGame=false



  def strongestMove(possible: Iterable[PossiblePattern]):Cell={
    val minMiss:Int = possible.map(_.missing.size).min
    val minPoss = possible.filter(_.missing.size == minMiss)

    if( minMiss==0 ){
      for( p <- minPoss ){
        printf("---Can win by:%n%s %n---------%n", p.pat.printView)
      }
      return minPoss.head.shiftedKillPoint
      //Thread sleep 100000
    }

    val counts = new mutable.HashMap[Cell,Int]
    for( m <- minPoss; mi <- m.shiftedMissing ){
      counts(mi) = counts.getOrElse(mi,0)+1
    }

    val bestPlaceOpt = counts.toArray.sortBy( - _._2 ).headOption
//    if( bestPlaceOpt.isEmpty ){
//      printf("strange! %s %n", minMiss)
//    }
    val bestPlace = bestPlaceOpt.get
    printf("best point %s count intersecting %d  minMiss=%d %n", bestPlace._1, bestPlace._2, minMiss)

    //val minMiss = myPossible.sortBy( _.missing.size ).head
    //printf("minMiss %s %n%s %n", minMiss.missing.size, minMiss.pat.printView)
    bestPlace._1
  }

  def pinside(board:Board, x:Int,y:Int)={
    x>=0 && y>=0 && x<board.getCols && y<board.getRows
  }

  def coalesce(threats:Iterable[MaterializedPattern], opponentMove:Cell):Pattern={
    for( t <- threats ){
      printf("-- sub-threat id=%s %n%s%n---%n", t.pat.idTrack, t.pat.printView)
    }
    val marks = for( t <- threats; m <- t.pat.marks ) yield m+t.shift
    val empty = for( t <- threats; m <- t.pat.empty ) yield m+t.shift
    Pattern( marks.toSet - opponentMove, empty.toSet + opponentMove, opponentMove )
  }

  def makeMove(board: Board, opponentMove: Square):Square = {
    val t0 = System.currentTimeMillis
    val m = makeMoveTimed(board, opponentMove)
    val t1 = System.currentTimeMillis
    printf("DefensivePatterns move compute time %s ms %n", t1 - t0)
    Thread.sleep(280)
    m
  }

  def lossToPatterns(threats: Seq[MaterializedPattern], opponentMove: Cell){
    def numberToIdSet(i:Int):Set[Int] = {
      val ret = for( j <- 0 until threats.size; if (i & (1<<j)) != 0 ) yield j
      //printf("numberToIdSet %d=%s %n",i,ret)
      ret.toSet
    }

    val threatSubsets = for( i <- 1 until (1<<threats.size) ) yield  numberToIdSet(i)

    val addedSymmetries = new mutable.HashSet[Pattern]
    val fruitfulIdSets = new mutable.HashSet[Set[Int]]

    printf("threatSubsets=%s %n",threatSubsets)
    for( threatSubsetIds <- threatSubsets.sortBy( _.size ) ){
      val notSuperset = fruitfulIdSets.forall( set => !set.subsetOf(threatSubsetIds) )
      if( notSuperset ){
        val threatSubset = threatSubsetIds map (id => threats(id))
        val moveCandidates = findMoveCandidates(threatSubset)
        val moves = findRuiningMoves(threatSubset, moveCandidates)
        if( moves.isEmpty ){
          printf("threatSubsetIds=%s %n",threatSubsetIds)
          val pnew:Pattern = coalesce(threatSubset, opponentMove)
          if( !(addedSymmetries contains pnew) ){
            printf("loss is expected, pnew from subset %s: %n%s%n------------%n",
              threatSubsetIds, pnew.printView)
            if( !pnew.continuous ){
              println("pattern NOT CONTINUOUS!")
            }

    //          for( t <- threats ){
    //            printf("derived from threat, pold: %n%s%n------------%n", t.pat.printView)
    //          }
            knownPatterns = knownPatterns + pnew
            addedSymmetries ++= pnew.allSymetries
            fruitfulIdSets += threatSubsetIds
            knownPatterns.savePattersToFile()
          }
        }
      }
    }
  }

  def findMoveCandidates(threats: Iterable[MaterializedPattern])={
    for( t <- threats; e <- t.pat.empty ) yield e + t.shift
  }

  def findRuiningMoves(threats: Iterable[MaterializedPattern], moveCandidates: Iterable[Cell])={
    moveCandidates filter { mc =>
      val remThreats = for( t <- threats; if !(t.ruinedBy(mc) ) ) yield t
      remThreats.isEmpty
    }
  }

  def makeMoveTimed(board: Board, opponentMove: Square):Square = {
    var m:Square=null

//    val p = new Pattern(Set(Cell(1,0),Cell(2,0),Cell(3,0),Cell(4,0)), Set(Cell(5,0))).zeroShift
//    val threats = findEnemyPatterns(board, Set(p))
    val threats = knownPatterns.findEnemyPatterns(board, piece)

    printf("enemy patterns count=%d %s %n", threats.size, threats )
    if( !threats.isEmpty ){
      val moveCandidates = findMoveCandidates(threats)
      val moves = findRuiningMoves(threats, moveCandidates)
      printf("moves for threats %s %n", moves )
      if( moves.isEmpty ){
        if( !losingGame ){
          lossToPatterns(threats, Cell(opponentMove.getCol, opponentMove.getRow))
        }else{
          println("losing game, not tracking new patterns")
        }

        losingGame=true

        val move = moveCandidates.head
        m = new NormalSquare(move.y, move.x);
      }else{
//      val ecell = threats.head.pat.empty.head
//      val move = ecell + threats.head.shift
        val move = moves.head
        m = new NormalSquare(move.y, move.x);
      }
    }

    if(m==null){
      val defensiveOnly=false

      if( defensiveOnly ){
        for( row <- 0 until board.getRows; col <- 0 until board.getCols ){
          if( m==null && board.getPiece(row, col)==0 ){
            m = new NormalSquare(row, col);
          }
        }
      }else{
        // attack
        val myPossible = knownPatterns.findMyPossiblePatterns(board, piece)
        val strongest = strongestMove(myPossible)
        m = new NormalSquare(strongest.y, strongest.x)
      }

    }
    m
  }


  def startNewGame = {}
}